home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / muzsrc1.zip / EDIT.CPP < prev    next >
C/C++ Source or Header  |  1992-07-21  |  16KB  |  465 lines

  1. // **********************************************
  2. // File: EDIT.CPP
  3. // Edit window input module
  4.  
  5. #include "muzika.h"
  6. #include <stdlib.h>
  7. #include <values.h>
  8.  
  9. // **********************************************
  10. // InsertEmptyStaff inserts a new empty staff in the specified list
  11. // given the staff Y location and the part multiplicity.
  12. // The list is kept sorted by the Y locations in ascending order.
  13.  
  14. void InsertEmptyStaff(IndexedList &staffList, unsigned Y, int multiplicity)
  15. {
  16.   // Find the index to insert at
  17.   for (int index = 0;
  18.     index < staffList.number() && ((Staff *) &staffList[index])->Y() < Y;
  19.     index += multiplicity);
  20.   if (index > staffList.number())
  21.     index = staffList.number();
  22.  
  23.   // Update coordinates of other staves and of the marked block
  24.   for (int other = index; other < staffList.number(); ++other)
  25.     ((Staff *) &staffList[other])->Y() += pixelsPerStaff;
  26.   if (markBeginStaff >= index)
  27.     ++markBeginStaff;
  28.   if (markEndStaff >= index)
  29.     ++markEndStaff;
  30.  
  31.   // Adjust Y to a multiple of pixelsPerStaff
  32.   if (index)
  33.     Y = ((Staff *) &staffList[index-1])->Y()+pixelsPerStaff;
  34.   else
  35.     Y = pixelsPerStaff;
  36.  
  37.   // Insert the staff
  38.   staffList.insertAt(*new Staff(Y), index);
  39. }
  40.  
  41. // **********************************************
  42. // NewMultipleStaff creates a new multiple staff, after the user
  43. // has clicked the pencil-on-staff symbol.
  44. // The staff is inserted in the list, and the scroll bar range
  45. // is readjusted.
  46.  
  47. void NewMultipleStaff(int Y)
  48. {
  49.   Part &p = *((Part *) &melody.part[displayedPart]);
  50.  
  51.   // Insert a new staff group in the database and refresh screen
  52.   for (int i = 0; i < p.multiplicity(); ++i)
  53.     InsertEmptyStaff(p.staff, Y+p.GetPartY(), p.multiplicity());
  54.   melodyModified = TRUE;
  55.   SetScrollRange(hEditWnd, SB_VERT, 0,
  56.     ((Staff *) &p.staff[p.staff.number()-1])->Y(), TRUE);
  57.   InvalidateRect(hEditWnd, NULL, TRUE);
  58. }
  59.  
  60. // **********************************************
  61. // IdentifyStaff finds the index of the staff to which the Y coordinate
  62. // (obtained from the current cursor position) is closest.
  63.  
  64. int IdentifyStaff(IndexedList &staffList, unsigned Y)
  65. {
  66.   unsigned minDistance = MAXINT;
  67.   int minIndex;
  68.   int staffY;
  69.  
  70.   // Find a staff that is closest to the clicked point,
  71.   // by minimizing the distance from different staves
  72.   for (int index = 0; index < staffList.number(); ++index) {
  73.     // If point actually on the staff, return it
  74.     if ((staffY = ((Staff *) &staffList[index])->Y()) <= Y &&
  75.       staffY+24 >= Y)
  76.       return index;
  77.     int distance = (staffY > Y) ? staffY-Y : Y-staffY-25;
  78.     if (distance < minDistance) {
  79.       minDistance = distance;
  80.       minIndex = index;
  81.     }
  82.   }
  83.  
  84.   // Return a staff only if within reasonable distance
  85.   return (minDistance < (pixelsPerStaff-25)/2) ? minIndex : -1;
  86. }
  87.  
  88. // **********************************************
  89. // DeleteMultipleStaff deletes the multiple staff identified
  90. // by a Y coordinate (obtained from the current cursor position).
  91. // If the staff contains any objects, the user is requested
  92. // to confirm the operation.
  93.  
  94. void DeleteMultipleStaff(int Y)
  95. {
  96.   // Obtain a pointer to the staff to delete
  97.   Part &p = *((Part *) &melody.part[displayedPart]);
  98.   int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
  99.   if (staffIndex < 0) return;
  100.   staffIndex = staffIndex/p.multiplicity()*p.multiplicity();
  101.   Staff *s = (Staff *) &p.staff[staffIndex];
  102.  
  103.   // Check if the multiple staff contains any objects
  104.   for (int i = staffIndex;
  105.     i/p.multiplicity() == staffIndex/p.multiplicity(); ++i) {
  106.     Staff *s = (Staff *) &p.staff[i];
  107.     if (s->pointObject.number() || s->continuousObject.number())
  108.       if (MessageBox(hEditWnd, "Staff is not empty. Erase anyway?", "WARNING",
  109.         MB_ICONEXCLAMATION | MB_YESNOCANCEL) == IDYES)
  110.         break;
  111.         else return;
  112.   }
  113.  
  114.   // Erase the staff
  115.   int staffY = s->Y();
  116.   for (i = 0; i < p.multiplicity(); ++i)
  117.     p.staff.destroyAt(staffIndex);
  118.   if (markBeginStaff > staffIndex)
  119.     markBeginStaff -= p.multiplicity();
  120.   if (markEndStaff >= staffIndex)
  121.     markEndStaff -= p.multiplicity();
  122.   if (markEndStaff < markBeginStaff)
  123.     UnmarkBlock();
  124.   if (p.staff.number() > staffIndex) {
  125.     int staffDiff = ((Staff *) &p.staff[staffIndex])->Y()-staffY;
  126.     for (; staffIndex < p.staff.number(); ++staffIndex)
  127.       ((Staff *) &p.staff[staffIndex])->Y() -= staffDiff;
  128.   }
  129.  
  130.   // Mark the melody as modified and readjust the scroll bar range
  131.   melodyModified = TRUE;
  132.   SetScrollRange(hEditWnd, SB_VERT, 0,
  133.     p.staff.number() ? ((Staff *) &p.staff[p.staff.number()-1])->Y() : 0, TRUE);
  134.   InvalidateRect(hEditWnd, NULL, TRUE);
  135. }
  136.  
  137. // **********************************************
  138. // NewPointObject creates a point object corresponding to the
  139. // active symbol at the given coordinate (obtained from the current
  140. // cursor position), and inserts it in the staff according
  141. // to the object _location attribute.
  142.  
  143. void NewPointObject(SymbolClass *symbol, int X, int Y)
  144. {
  145.   // Obtain the staff to insert the object into
  146.   Part &p = *((Part *) &melody.part[displayedPart]);
  147.   int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
  148.   Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
  149.   if (!s || X < s->X() || X >= s->X()+s->width()) return;
  150.  
  151.   // Create the object and decide where to insert it
  152.   int first, last;
  153.   MusicalObject *obj = symbol->CreateObject(staffIndex, WidthRound(X-s->X()), Y -= s->Y());
  154.   if (obj) {
  155.     X = ((PointObject *) obj)->X();
  156.  
  157.     // Check the object _location attribute
  158.     switch (obj->location() & ~ONEPERSTAFF) {
  159.       case INSTAFF:
  160.       case ABOVESTAFF:
  161.       case BELOWSTAFF:
  162.         first = last = staffIndex;
  163.         break;
  164.  
  165.       case ABOVEMULTIPLE:
  166.         first = last = staffIndex/p.multiplicity()*p.multiplicity();
  167.         break;
  168.  
  169.       case BELOWMULTIPLE:
  170.         first = last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
  171.         break;
  172.  
  173.       case COMMONMULTIPLE:
  174.         first = staffIndex/p.multiplicity()*p.multiplicity();
  175.         last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
  176.         break;
  177.     }
  178.  
  179.     // Insert the objects in the required staff or staves
  180.     for (staffIndex = first; staffIndex <= last; ++staffIndex) {
  181.       s = (Staff *) &p.staff[staffIndex];
  182.       IndexedList &list = s->pointObject;
  183.       PointObject *obj1;
  184.       for (int index = 0;
  185.         index < list.number() && (obj1 = (PointObject *) &list[index])->X() <= X;
  186.         ++index)
  187.         if (obj1->location() & ONEPERSTAFF && obj1->X() == X) {
  188.           MessageBox(hEditWnd, "Only one such symbol is allowed per staff", NULL,
  189.             MB_ICONEXCLAMATION | MB_OK);
  190.           return;
  191.         }
  192.       list.insertAt(
  193.         (staffIndex == first) ? *obj : *symbol->CreateObject(staffIndex, X, Y), index);
  194.     }
  195.   }
  196.   melodyModified = TRUE;
  197.  
  198.   // Refresh screen
  199.   InvalidateRect(hEditWnd, NULL, !obj);
  200. }
  201.  
  202. // **********************************************
  203. // NewContinuousObject creates a continuous object corresponding to the
  204. // active symbol at the given coordinate (obtained from the current
  205. // cursor position), and inserts it in the staff according
  206. // to the object _location attribute.
  207.  
  208. void NewContinuousObject(SymbolClass *symbol, int Xleft, int Xright, int Y)
  209. {
  210.   // Obtain the staff to insert the object into
  211.   Part &p = *((Part *) &melody.part[displayedPart]);
  212.   int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
  213.   Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
  214.   if (!s || Xleft < s->X() || Xright >= s->X()+s->width()) return;
  215.  
  216.   // Create the object and decide where to insert it
  217.   int first, last;
  218.   MusicalObject *obj =
  219.     symbol->CreateObject(staffIndex, WidthRound(Xleft-s->X()), WidthRound(Xright-s->X()));
  220.   if (obj) {
  221.     Xleft = ((ContinuousObject *) obj)->Xleft();
  222.     Xright = ((ContinuousObject *) obj)->Xright();
  223.  
  224.     // Check the object _location attribute
  225.     switch (obj->location() & ~ONEPERSTAFF) {
  226.       case INSTAFF:
  227.       case ABOVESTAFF:
  228.       case BELOWSTAFF:
  229.         first = last = staffIndex;
  230.         break;
  231.  
  232.       case ABOVEMULTIPLE:
  233.         first = last = staffIndex/p.multiplicity()*p.multiplicity();
  234.         break;
  235.  
  236.       case BELOWMULTIPLE:
  237.         first = last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
  238.         break;
  239.  
  240.       case COMMONMULTIPLE:
  241.         first = staffIndex/p.multiplicity()*p.multiplicity();
  242.         last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
  243.         break;
  244.     }
  245.  
  246.     // Insert the object in the appropriate staff or staves
  247.     for (staffIndex = first; staffIndex <= last; ++staffIndex) {
  248.       s = (Staff *) &p.staff[staffIndex];
  249.       IndexedList &list = s->continuousObject;
  250.       ContinuousObject *obj1;
  251.       for (int index = 0;
  252.         index < list.number() &&
  253.           (obj1 = (ContinuousObject *) &list[index])->Xleft() <= Xleft;
  254.         ++index)
  255.         if (obj1->location() & ONEPERSTAFF && obj1->Xleft() == Xleft) {
  256.           MessageBox(hEditWnd, "Only one such symbol is allowed per staff", NULL,
  257.             MB_ICONEXCLAMATION | MB_OK);
  258.           return;
  259.         }
  260.       list.insertAt((staffIndex == first) ? *obj :
  261.         *symbol->CreateObject(staffIndex, Xleft, Xright), index);
  262.     }
  263.   }
  264.   melodyModified = TRUE;
  265.  
  266.   // Refresh screen
  267.   InvalidateRect(hEditWnd, NULL, !obj);
  268. }
  269.  
  270. // **********************************************
  271. // DeleteMusicalObject deletes the objects that are within
  272. // (pixelsPerObject/2) pixels away from the given coordinate
  273. // (obtained from the current cursor position).
  274.  
  275. void DeleteMusicalObject(int X, int Y)
  276. {
  277.   // Obtain the staff to delete the objects from
  278.   Part &p = *((Part *) &melody.part[displayedPart]);
  279.   int staffIndex = IdentifyStaff(p.staff, (Y += p.GetPartY()));
  280.   Staff *s = (staffIndex >= 0) ? (Staff *) &p.staff[staffIndex] : NULL;
  281.   if (!s || X < s->X() || X >= s->X()+s->width()) return;
  282.  
  283.   // Scan the point objects list and delete any objects in range
  284.   X -= s->X();
  285.   Y -= s->Y();
  286.   for (int index = 0; index < s->pointObject.number(); ++index) {
  287.     PointObject *obj = (PointObject *) &s->pointObject[index];
  288.     if (abs(obj->X()-X) < pixelsPerObject/2)
  289.  
  290.       // Check the object _location attribute to see if
  291.       // any special treatment is required
  292.       switch (obj->location() & ~ONEPERSTAFF) {
  293.         case ABOVESTAFF:
  294.         case ABOVEMULTIPLE:
  295.           if (Y < 0) {
  296.             s->pointObject.destroyAt(index);
  297.             --index;
  298.           }
  299.           break;
  300.  
  301.         case BELOWSTAFF:
  302.         case BELOWMULTIPLE:
  303.           if (Y > 24) {
  304.             s->pointObject.destroyAt(index);
  305.             --index;
  306.           }
  307.           break;
  308.  
  309.         case INSTAFF:
  310.           s->pointObject.destroyAt(index);
  311.           --index;
  312.           break;
  313.       }
  314.   }
  315.  
  316.   // Scan the continuous objects list and delete any objects in range
  317.   for (index = 0; index < s->continuousObject.number(); ++index) {
  318.     ContinuousObject *obj = (ContinuousObject *) &s->continuousObject[index];
  319.     if (abs(obj->Xleft()-X) < pixelsPerObject/2)
  320.       switch (obj->location() & ~ONEPERSTAFF) {
  321.         case ABOVESTAFF:
  322.         case ABOVEMULTIPLE:
  323.           if (Y < 0) {
  324.             s->continuousObject.destroyAt(index);
  325.             --index;
  326.             melodyModified = TRUE;
  327.           }
  328.           break;
  329.  
  330.         case BELOWSTAFF:
  331.         case BELOWMULTIPLE:
  332.           if (Y > 24) {
  333.             s->continuousObject.destroyAt(index);
  334.             --index;
  335.             melodyModified = TRUE;
  336.           }
  337.           break;
  338.  
  339.         case INSTAFF:
  340.           s->continuousObject.destroyAt(index);
  341.           --index;
  342.           melodyModified = TRUE;
  343.           break;
  344.       }
  345.   }
  346.  
  347.   // Scan all staves in group and delete COMMONMULTIPLE objects
  348.   int first = staffIndex/p.multiplicity()*p.multiplicity();
  349.   int last = (staffIndex/p.multiplicity()+1)*p.multiplicity()-1;
  350.   for (staffIndex = first; staffIndex <= last; ++staffIndex) {
  351.     // Delete COMMONMULTIPLE point objects
  352.     s = (Staff *) &p.staff[staffIndex];
  353.     for (index = 0; index < s->pointObject.number(); ++index) {
  354.       PointObject *obj = (PointObject *) &s->pointObject[index];
  355.       if (abs(obj->X()-X) < pixelsPerObject/2 &&
  356.         (obj->location() & ~ONEPERSTAFF) == COMMONMULTIPLE) {
  357.         s->pointObject.destroyAt(index);
  358.         --index;
  359.         melodyModified = TRUE;
  360.       }
  361.     }
  362.     for (index = 0; index < s->continuousObject.number(); ++index) {
  363.       // Delete COMMONMULTIPLE continuous objects
  364.       ContinuousObject *obj = (ContinuousObject *) &s->continuousObject[index];
  365.       if (abs(obj->Xleft()-X) < pixelsPerObject/2 &&
  366.         (obj->location() & ~ONEPERSTAFF) == COMMONMULTIPLE) {
  367.         s->continuousObject.destroyAt(index);
  368.         --index;
  369.         melodyModified = TRUE;
  370.       }
  371.     }
  372.   }
  373.  
  374.   // Refresh screen
  375.   InvalidateRect(hEditWnd, NULL, TRUE);
  376. }
  377.  
  378. // **********************************************
  379. // MoveStaff moves a staff from Yfrom to Yto, both coordinates
  380. // obtained from the mouse cursor. Before actually moving,
  381. // the destination is checked to be free from other staves.
  382.  
  383. void MoveStaff(int Yfrom, int Yto)
  384. {
  385.   // Obtain a pointer to the staff to move
  386.   Part &p = *((Part *) &melody.part[displayedPart]);
  387.   int staffIndex = IdentifyStaff(p.staff, (Yfrom += p.GetPartY()));
  388.   if (staffIndex < 0) return;
  389.   staffIndex = staffIndex/p.multiplicity()*p.multiplicity();
  390.   Staff *s = (Staff *) &p.staff[staffIndex];
  391.   int lastMoved = staffIndex+p.multiplicity()-1;
  392.  
  393.   // Verify that the destination is not occupied
  394.   Yto += p.GetPartY();
  395.   for (int indexTo = 0; indexTo < p.staff.number(); indexTo += p.multiplicity())
  396.     if (indexTo != staffIndex) {
  397.       Staff &firstTo = *((Staff *) &p.staff[indexTo]);
  398.       Staff &lastTo = *((Staff *) &p.staff[indexTo+p.multiplicity()-1]);
  399.       if (Yto <= lastTo.Y()+24 &&
  400.         ((Staff *) &p.staff[lastMoved])->Y()-s->Y()+Yto+24 >= firstTo.Y()) {
  401.         // The destination is occupied:
  402.         // display an error message
  403.         MessageBox(hEditWnd, "Cannot move onto another staff", NULL,
  404.           MB_ICONEXCLAMATION | MB_OK);
  405.         return;
  406.     }
  407.   }
  408.  
  409.   // Set the staff new coordinates and move it
  410.   int distance = Yto-s->Y();
  411.   for (; staffIndex <= lastMoved; ++staffIndex)
  412.     ((Staff *) &p.staff[staffIndex])->Y() += distance;
  413.   staffIndex -= p.multiplicity();
  414.   for (indexTo = 0;
  415.     indexTo < p.staff.number() && ((Staff *) &p.staff[indexTo])->Y() < Yto;
  416.     indexTo += p.multiplicity());
  417.   while (staffIndex <= lastMoved) {
  418.     s = (Staff *) &p.staff[staffIndex];
  419.  
  420.     // Detach and re-insert staves whose index is between
  421.     // the old and new indexes of the moved staff
  422.     p.staff.detachAt(staffIndex);
  423.     if (indexTo > staffIndex) {
  424.       --lastMoved;
  425.       --indexTo;
  426.     }
  427.     p.staff.insertAt(*s, indexTo);
  428.     if (staffIndex >= indexTo) {
  429.       ++staffIndex;
  430.       ++indexTo;
  431.     }
  432.   }
  433.  
  434.   // Refresh the display
  435.   UnmarkBlock();
  436.   melodyModified = TRUE;
  437.   InvalidateRect(hEditWnd, NULL, TRUE);
  438.   SetScrollRange(hEditWnd, SB_VERT, 0,
  439.     ((Staff *) &p.staff[p.staff.number()-1])->Y(), TRUE);
  440. }
  441.  
  442. // **********************************************
  443. // MoveMusicalObject moves a musical object by using
  444. // the CutBlock and PasteBlock functions in BLOCK.CPP.
  445.  
  446. void MoveMusicalObject(int Xfrom, int Yfrom, int Xto, int Yto)
  447. {
  448.   // Verify both points are on the same staff
  449.   Part &p = *((Part *) &melody.part[displayedPart]);
  450.   int staffFrom = IdentifyStaff(p.staff, Yfrom+p.GetPartY());
  451.   int staffTo = IdentifyStaff(p.staff, Yto+p.GetPartY());
  452.   if (staffFrom < 0 || staffTo < 0)
  453.     return;
  454.   staffFrom = staffFrom/p.multiplicity()*p.multiplicity();
  455.   staffTo = staffTo/p.multiplicity()*p.multiplicity();
  456.   int staffX = ((Staff *) &p.staff[staffFrom])->X();
  457.  
  458.   // Use the cut and paste functions to move the objects
  459.   MarkBlock(staffFrom, WidthRound(Xfrom-staffX),
  460.     staffFrom, WidthRound(Xfrom-staffX));
  461.   CutBlock();
  462.   PasteBlock(staffTo, WidthRound(Xto-staffX));
  463.   InvalidateRect(hEditWnd, NULL, TRUE);
  464. }
  465.